/*******************************************************************

Copyright (c) 2004 Open Mobile Alliance, Ltd.  All Rights Reserved.

This software is made available under the usage and distribution terms 
covered by the Common Public License 1.0 and by using this software you
are bound by the terms and conditions of the same.

EXCEPT AS EXPRESSLY SET FORTH IN THE COMMON PUBLIC LICENSE,
THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
   
Each recipient is solely responsible for determining the 
appropriateness of using and distributing this program and assumes 
all risks associated with its exercise of rights under the Common 
Public License, including but not limited to the risks and 
costs of program errors, compliance with applicable laws, 
damage to or loss of data, programs or equipment, and 
unavailability or interruption of operations.

********************************************************************/

#include "SctsThread.h"
#include "Sml.h"

#ifdef __SCTS_PLATFORM_WIN32__

DWORD WINAPI ThreadProc(LPVOID lpParam)
{
	int ret;
	ret=((SctsThread *)lpParam)->Run();
	((SctsThread *)lpParam)->Exit();
 	return ret;
}

/*****************************************************************
SctsMutex
******************************************************************/
std::map<int,SctsThread *> SctsThread::threads;
SctsMutex SctsThread::threadsLock;

bool SctsMutex::Lock(unsigned long timeout) 
{
	if (WaitForSingleObject(handle,timeout)==WAIT_TIMEOUT) return false;
	return true;
}

bool SctsMutex::Unlock() 
{
	if (ReleaseMutex(handle)) return true;
	return false;
}

SctsMutex::SctsMutex(bool initialOwner) 
{
	handle=CreateMutex(0,initialOwner,0);
	SCTSASSERT(handle);
}

SctsMutex::~SctsMutex() 
{
	handle=(void *)CloseHandle(handle);
	SCTSASSERT(handle);
}

/*****************************************************************
SctsSemaphore
******************************************************************/
bool SctsSemaphore::Lock(unsigned long timeout) 
{
	if (WaitForSingleObject(handle,timeout)==WAIT_TIMEOUT) return false;
	return true;
}

bool SctsSemaphore::Unlock() 
{
	if (ReleaseSemaphore(handle,1,0)) return true;
	return false;
}

SctsSemaphore::SctsSemaphore(unsigned long initialCount,unsigned long maximumCount)
{
	SCTSASSERT(maximumCount);
	SCTSASSERT(initialCount<=maximumCount);
	handle=CreateSemaphore(0,initialCount,maximumCount,0);
	SCTSASSERT(handle);
}

SctsSemaphore::~SctsSemaphore() 
{
	handle=(void *)CloseHandle(handle);
	SCTSASSERT(handle);
}

/*****************************************************************
SctsThread
******************************************************************/
SctsThread::~SctsThread()
{
	threadsLock.Lock();
	threads.erase(threads.find(id));
	threadsLock.Unlock();
}

int SctsThread::GetThreadCount(SctsThreadType _type)
{
	int cnt=0;
	std::map<int,SctsThread *>::iterator it;
	threadsLock.Lock();
	for (it=threads.begin();it!=threads.end();++it)
	{
		if (_type==UNKNOWN_THREAD || it->second->GetType()==_type) ++cnt;
	}
	threadsLock.Unlock();
	return cnt;
}

int SctsThread::Run() 
{
	return 0;
}

int SctsThread::Init() 
{
	return 0;
}

int SctsThread::Exit() 
{
	return 0;
}

void SctsThread::Sleep(unsigned int milliseconds) 
{
	::Sleep(milliseconds);
}

int SctsThread::GetID() 
{
	return::GetCurrentThreadId();
}
int SctsThread::GetProcessID() 
{
	return ::GetCurrentProcessId();
}

bool SctsThread::Suspend() 
{
	if (SuspendThread(handle)) return true;
	return false;
}

bool SctsThread::Resume()
{
	if (ResumeThread(handle)) return true;
	return false;
}

void SctsThread::SetTerminate(bool _terminate) 
{
	terminateLock.Lock(-1);
	terminate=_terminate;
	terminateLock.Unlock();
}

bool SctsThread::GetTerminate()
{
	bool _terminate;
	terminateLock.Lock(-1);
	_terminate=terminate;
	terminateLock.Unlock();
	return _terminate;
}

int SctsThread::Create(bool createSuspended)
{
	int ret;
	ret=Init();
	if (ret) return ret;
	handle=CreateThread(0,0,ThreadProc,this,createSuspended ? CREATE_SUSPENDED : 0,&id);
	if (!handle)
	{
		sctsStatusLog("Unable to create thread.\r\n");
		SCTSASSERT(false);
	}
	threadsLock.Lock();
	threads[id]=this;
	threadsLock.Unlock();
	return 0;
}

SctsThread::SctsThread(SctsThreadType _type) : type(_type) 
{
	terminate=false;
}

int sctsGetTerminate()
{
	std::map <int,SctsThread *>::iterator it;
	SctsThread::threadsLock.Lock();
	it=SctsThread::threads.find(SctsThread::GetID());
	SctsThread::threadsLock.Unlock();
	if (it==SctsThread::threads.end()) return false;
	return it->second->GetTerminate();
}

#endif

/*****************************************************************
Status logging functions
******************************************************************/
#include "map"
#include "list"

static std::map <int,char *> activeStatusLogs;
static std::list <char *> inactiveStatusLogs;
static SctsMutex statusLogsLock;

void sctsStatusLogOpen()
{
	char *buffer;
	statusLogsLock.Lock();
	if (activeStatusLogs.find(SctsThread::GetID())!=activeStatusLogs.end())
	{
		statusLogsLock.Unlock();
		return;
	}
	buffer=(char *)sctsMalloc(sizeof(char *)*4+5);
	sprintf(buffer,"%x%x.log",SctsThread::GetProcessID(),buffer);
	activeStatusLogs[SctsThread::GetID()]=buffer;
	statusLogsLock.Unlock();
}

void sctsStatusLogClose()
{
	std::map <int,char *>::iterator it;
	statusLogsLock.Lock();
	if ((it=activeStatusLogs.find(SctsThread::GetID()))==activeStatusLogs.end())
	{
		statusLogsLock.Unlock();
		return;
	}
	inactiveStatusLogs.push_back(it->second);
	activeStatusLogs.erase(it);
	statusLogsLock.Unlock();
}

extern "C"
{
void sctsStatusLog(char *status,long size)
{
	FILE *fp;
	char *statusLog;
	std::map <int,char *>::iterator it;
	statusLogsLock.Lock();
	if ((it=activeStatusLogs.find(SctsThread::GetID()))==activeStatusLogs.end())
	{
		statusLogsLock.Unlock();
		return;
	}
	statusLog=it->second;
	statusLogsLock.Unlock();
	if (!size) size=sctsStrlen(status);
	fp=fopen(statusLog,"a+b");
	fwrite(status,sizeof(char),size,fp);
	fclose(fp);
}
}

void sctsStatusLog(bool success)
{
	if (success)
		sctsStatusLog("Ok\r\n");
	else
		sctsStatusLog("Error\r\n");
}

void sctsStatusLog(int i)
{
	char buffer[10];
	sctsInt2Str(i,buffer,16);
	sctsStatusLog(buffer);
}

/*****************************************************************
Terminate all threads and delete status logs
******************************************************************/
bool sctsTerminate()
{
	std::map <int,char *>::iterator lit1; activeStatusLogs;
	std::list <char *>::iterator lit2; inactiveStatusLogs;
	std::map<int,SctsThread *>::iterator it;
	SctsThread::threadsLock.Lock();
	bool noSessions=true;
	/* Terminate all session threads first */
	if ((it=SctsThread::threads.begin())!=SctsThread::threads.end())
	{
		for (;it!=SctsThread::threads.end();it++) 
		{
			if (it->second->GetType()!=SESSION_THREAD) continue;
			it->second->SetTerminate(true);
			noSessions=false;
		}
	}
	if (!noSessions)
	{
		SctsThread::threadsLock.Unlock();
		return false;
	}
	/* Terminate all the remaining threads */
	if ((it=SctsThread::threads.begin())!=SctsThread::threads.end())
	{
		for (;it!=SctsThread::threads.end();it++) it->second->SetTerminate(true);
		SctsThread::threadsLock.Unlock();
		return false;
	}
	/* Terminate the RTK */
	smlTerminate();
	/* Delete all the log files */
	statusLogsLock.Lock();
	for (lit1=activeStatusLogs.begin();lit1!=activeStatusLogs.end();lit1++) remove(lit1->second);
	for (lit2=inactiveStatusLogs.begin();lit2!=inactiveStatusLogs.end();lit2++) remove(*lit2);
	return true;
}

SctsThreadType SctsThread::GetType()
{
	return type;
}

void sctsAssert(int exp,char *file,int line)
{
	char buffer[100];
	if (!exp)
	{
		sprintf(buffer,"Assertion failure at line : %d, file : %s.",line,file);
		sctsStatusLog(buffer);
		MessageBox(0,buffer,"Assertion Failure",MB_ICONERROR);
		abort();
	}
}



	

